/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-10-16 15:50:50
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-04 15:59:29
 * @FilePath: /FileBackup/include/fileBackupUnit.hpp
 * @Description: 业务集成类
 * - 集成对文件的基本操作
 * - 1. 根据文件路径获取文件的 `stat` 结构体，解析其中的信息
 * - 2. 文件类型定义分析
 * - 3. 封装基本文件操作
 * - 4. CRC 校验码计算
 */
#pragma once

#include "argumentsParser.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fstream>
#include <fcntl.h>

struct CompressHeader // 数据压缩文件头
{
    char filePath[COMPRESS_FILE_PATH]; // 文件原始路径
    uint32_t CRC;                      // 文件校验码（压缩前）
    uint32_t offset;                   // 文件头偏移量
    off_t fileSize;                    // 压缩文件原始大小
};

struct BackupHeader // 数据备份文件头 512 bytes
{
    char filePath[TAR_FILE_PATH];  // 文件原始路径
    uint32_t CRC;                  // 文件校验码
    uint32_t offset;               // 文件头偏移量
    char message[PAK_MESSAGE_LEN]; // 文件表述信息
};

struct FileHeader // 文件头信息 512 bytes
{
    char filePath[TAR_FILE_PATH]; // 文件相对路径
    uint32_t CRC;                 // 文件校验码
    uint32_t blockNum;            // 打包后文件包含的块数
    struct stat metadata;         // 文件元数据
    char pad[TAR_PAD_SIZE];       // 填充剩余部分
};

union RecordBlock // 数据记录块 512 bytes
{
    struct FileHeader fileHeader;    // 文件头
    char dataBlock[TAR_RECORD_SIZE]; // 数据块
};

class FileBaseUnit : public std::fstream
{
public:
    /* 文件基本信息 */
    FileHeader fileHeader;
    std::filesystem::path filePath;

    /// @brief 检查文件指针偏移量
    /// @param span 读写偏移
    /// @return 没有错位 `true`
    inline bool CheckFileOffset(int span)
    {
        return this->tellp() % span == 0;
    }

    /// @brief 计算CRC-32校验码
    /// @param data 计算数据
    /// @param length 数据长度
    /// @return CRC校验码
    static uint32_t Calc_CRC32(uint8_t *data, size_t length);

    /// @brief 根据 `FileFilterArguments` 与 `FileHeader` 过滤文件
    /// @param filter_arguments 文件过滤参数
    /// @return 满足 `filter_arguments` 为 `true`
    bool Filter(const FileFilterArguments &filter_arguments);

    /// @brief 获取文件类型
    /// @return `FileType` 文件类型
    FileType GetFileType();
    static FileType GetFileType(const FileHeader &file_header);

    /// @brief 读取备份文件头信息
    /// @param backup_header 返回数据
    /// @return 读取成功 `true`
    bool ReadBackupHeader(BackupHeader &backup_header);

    /// @brief 读取压缩文件头信息
    /// @param compress_header 返回数据
    /// @return 读取成功 `true`
    bool ReadCompressHeader(CompressHeader &compress_header);

    /// @brief 向当前文件流写入文件头 & 累加计算文件头的校验码
    /// @param file_header 源文件的文件头 `FileHeader`
    void WriteFileHeader(const FileHeader &file_header);

    /// @brief 读取文件头信息 & 保证文件头不为空
    /// @return `FileHeader` 为空 `false`
    bool ReadFileHeader(FileHeader &file_header);

    /// @brief 写入文件数据部分 & 累加计算校验码
    /// @param source_file 源文件的数据流
    void WriteDataBlock(FileBaseUnit &source_file);

    /// @brief 还原文件数据部分
    /// @param file_data 打包文件输入数据流
    /// @return 文件数据还原 & 数据校验
    bool RestoreNormalFileData(FileBaseUnit &file_data);

    /// @brief 保存符号链接文件的路径信息 使用一个数据块
    /// @param file_path 符号链接文件路径
    void WriteSymbolicLink(const std::filesystem::path &file_path);

    /// @brief 保存硬链接的路径信息 使用一个数据块
    /// @param file_path 硬链接文件路径
    void WriteHardLink(const std::filesystem::path &file_path);

    /// @brief 读取保存符号链接的数据块
    /// @return 符号链接文件路径
    std::filesystem::path ReadFileLink();

    /// @brief 还原文件元数据
    void RestoreMetadata();

    /// @brief 向文件头写入校验码
    /// @param CRC_value 校验码具体值
    /// @param file_head_pos 文件头的偏移量
    void WriteCRC(uint32_t CRC_value, std::streampos file_head_pos);

    /// @brief 更新压缩文件头
    /// @param compress_header 文件头信息
    void UpdateCompressHeader(const CompressHeader &compress_header);

public:
    /// @brief 解析文件信息 `FileHeader` & 创建文件读取流
    /// @param file_path 相对于源文件的路径
    FileBaseUnit(const std::filesystem::path &file_path);

    /// @brief 使用 `FileHeader` 还原原始文件头相关数据
    /// @param file_header 文件头 `FileHeader`
    FileBaseUnit(const FileHeader &file_header);

    /// @brief 构建备份文件 & 写入文件头 `BackupHeader`
    /// @param file_path 备份文件目标目录
    /// @param backup_header 备份文件信息
    FileBaseUnit(const std::filesystem::path &file_path,
                 BackupHeader &backup_header);

    /// @brief 构建压缩文件（文件头最后写入）
    /// @param file_path 压缩文件目标路径
    /// @param compress_header 压缩文件信息
    FileBaseUnit(const std::filesystem::path &file_path,
                 CompressHeader &compress_header);

    ~FileBaseUnit();
};